﻿namespace HZY.Framework.DynamicApiController.Core;

/// <summary>
/// 应用程序模型约定转换处理
/// </summary>
public class DynamicApiControllerApplicationModelConvention : IApplicationModelConvention
{
    private const string AsyncString = "Async";
    private readonly DynamicApiControllerOptions _dynamicApiControllerOptions;
    private readonly MvcOptions _mvcOptions;

    /// <summary>
    /// 获取api默认前缀 路由对象
    /// </summary>
    /// <returns></returns>
    private static RouteAttribute DefaultRouteAttribute => new($"[controller]/[action]");

    /// <summary>
    /// 应用程序模型约定转换处理
    /// </summary>
    /// <param name="dynamicApiControllerOptions">配置参数</param>
    /// <param name="mvcOptions">mvc 参数配置</param>
    public DynamicApiControllerApplicationModelConvention(DynamicApiControllerOptions dynamicApiControllerOptions,
        MvcOptions mvcOptions)
    {
        _dynamicApiControllerOptions = dynamicApiControllerOptions;
        _mvcOptions = mvcOptions;
    }

    /// <summary>
    /// 使用
    /// </summary>
    /// <param name="application"></param>
    public void Apply(ApplicationModel application)
    {
        var controllers = application.Controllers
            //排除 Controller 结尾的文件
            //.Where(w => !w.ControllerType.Name.EndsWith(DynamicApiControllerTypeNameSuffixEnum.Controller.ToString(), StringComparison.OrdinalIgnoreCase))
            ;

        foreach (var item in controllers)
        {
            RemoveEmptySelectors(item.Selectors);

            // 处理控制器名称
            item.ControllerName = GetControllerName(item);

            foreach (var action in item.Actions)
            {
                RemoveEmptySelectors(action.Selectors);

                // 处理函数名称
                action.ActionName = GetActionName(action);

                HandleActionRoute(action);
            }
        }
    }

    /// <summary>
    /// 移除空的选择器 防止干扰
    /// </summary>
    /// <param name="selectors"></param>
    private static void RemoveEmptySelectors(IList<SelectorModel> selectors)
    {
        for (var i = selectors.Count - 1; i >= 0; i--)
        {
            var selector = selectors[i];
            if (selector.AttributeRouteModel == null &&
                selector.ActionConstraints.Count <= 0 &&
                selector.EndpointMetadata.Count <= 0)
            {
                selectors.Remove(selector);
            }
        }
    }

    /// <summary>
    /// 处理 Action
    /// </summary>
    /// <param name="actionModel"></param>
    private void HandleActionRoute(ActionModel actionModel)
    {
        if (actionModel.Selectors.Count <= 0)
        {
            // 创建路由选择器
            var template = GetRouteTemplate(actionModel);
            var selector = new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel(template)
            };

            selector.ActionConstraints.Add(new HttpMethodActionConstraint([GetHttpMethodName(actionModel)]));
            actionModel.Selectors.Add(selector);

            return;
        }

        // 变更路由选择器
        foreach (var selectorModel in actionModel.Selectors)
        {
            //如果 action 找不到路由标签
            // selectorModel.AttributeRouteModel = selectorModel.AttributeRouteModel == null
            //     ? new AttributeRouteModel(GetRouteTemplate(actionModel))
            //     : GetAttributeRouteModel(actionModel, selectorModel.AttributeRouteModel);

            selectorModel.AttributeRouteModel = new AttributeRouteModel(GetRouteTemplate(actionModel));

            var httpMethodActionConstraint = selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>();
            if (httpMethodActionConstraint.FirstOrDefault()?.HttpMethods.FirstOrDefault() == null)
            {
                selectorModel.ActionConstraints.Add(
                    new HttpMethodActionConstraint([GetHttpMethodName(actionModel)]));
            }
        }
    }

    /// <summary>
    /// 获取路由模板地址
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private static IRouteTemplateProvider GetRouteTemplate(ActionModel actionModel)
    {
        // 如果 action 上面已经有了 路由标签
        if (actionModel.Attributes.FirstOrDefault(w => w is RouteAttribute) is RouteAttribute
            actionRouteAttribute)
        {
            return actionRouteAttribute;
        }

        // 如果 action 找不到路由标签 但是 控制器上面有路由标签 (如果是自定义路由)
        return GetRouteAttributeByControllerRouteAttribute(actionModel);
    }

    /// <summary>
    /// 根据控制器路由特性获取新的路由特性标记对象
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private static IRouteTemplateProvider GetRouteAttributeByControllerRouteAttribute(ActionModel actionModel)
    {
        var firstOrDefault = actionModel.Controller.Attributes.FirstOrDefault(w => w is IRouteTemplateProvider);
        if (firstOrDefault is not IRouteTemplateProvider controllerRouteAttribute)
        {
            controllerRouteAttribute = DefaultRouteAttribute;
        }


        var routeTemplate = (controllerRouteAttribute.Template ?? "").StartsWith('/')
            ? (controllerRouteAttribute.Template ?? "")
            : "/" + controllerRouteAttribute.Template;

        // 检查 actionModel 是否有 HttpMethodAttribute
        var httpMethodAttributes = GetHttpMethodAttributes(actionModel);
        if (httpMethodAttributes.Count > 0)
        {
            //处理 带有 http method 标记 如果标记中有模版那么直接拼接上去
            var httpMethodAttribute = httpMethodAttributes.FirstOrDefault();
            if (httpMethodAttribute != null && !string.IsNullOrWhiteSpace(httpMethodAttribute.Template))
            {
                // 如果 模版尾部有斜杠 /
                routeTemplate = routeTemplate.TrimEnd('/');
                routeTemplate += (httpMethodAttribute.Template.StartsWith('/') ? "" : "/") +
                    httpMethodAttribute.Template;
            }
        }

        var routeAttribute = new RouteAttribute(routeTemplate)
        {
            Name = controllerRouteAttribute.Name,
            Order = controllerRouteAttribute.Order ?? 0
        };
        return routeAttribute;
    }

    /// <summary>
    /// 获取 http method 请求方式
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private string GetHttpMethodName(ActionModel actionModel)
    {
        var actionName = actionModel.ActionName.ToLower();
        var result = nameof(DynamicApiControllerHttpMethodEnum.POST);

        foreach (var item in _dynamicApiControllerOptions.HttpMethodRule
                     .Where(item => actionName.StartsWith(item.Key)))
        {
            result = item.Value.ToString();
        }

        return result;
    }

    /// <summary>
    /// 获取控制器名称
    /// </summary>
    /// <param name="controllerModel"></param>
    /// <returns></returns>
    private string GetControllerName(ControllerModel controllerModel)
    {
        var controllerName = controllerModel.ControllerName;
        var result = controllerName;
        foreach (var item in _dynamicApiControllerOptions.ControllerTypeNameSuffixs)
        {
            if (!controllerName.EndsWith(item, StringComparison.OrdinalIgnoreCase)) continue;
            result = controllerName[..^item.Length];
        }

        return _dynamicApiControllerOptions.IsLower ? DynamicApiControllerExtensions.HumpTurnBar(result) : result;
    }

    /// <summary>
    /// 获取action 名称
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns>AliasName=别名 、ActionName=当前action名称</returns>
    private string GetActionName(ActionModel actionModel)
    {
        var httpMethodAttributes = GetHttpMethodAttributes(actionModel);
        var name = httpMethodAttributes.FirstOrDefault()?.Name;
        var actionName = actionModel.ActionName;

        if (_mvcOptions.SuppressAsyncSuffixInActionNames && actionName.EndsWith(AsyncString))
        {
            actionName = actionName[..^AsyncString.Length];
        }

        actionName = string.IsNullOrWhiteSpace(name) ? actionName : name;

        return _dynamicApiControllerOptions.IsLower
            ? DynamicApiControllerExtensions.HumpTurnBar(actionName)
            : actionName;
    }

    /// <summary>
    /// 获取 action 上面的http method 特性
    /// </summary>
    /// <param name="actionModel"></param>
    /// <returns></returns>
    private static List<HttpMethodAttribute> GetHttpMethodAttributes(ActionModel actionModel)
    {
        return actionModel.Attributes
                .Where(w => w is HttpMethodAttribute)
                .OrderByDescending(w => ((HttpMethodAttribute)w).Order)
                .Select(w => (HttpMethodAttribute)w)
                .ToList()
            ;
    }
}